package org.budo.weixin.api;

import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.budo.support.assertion.Assert;
import org.budo.support.http.client.HttpUtil;
import org.budo.support.http.client.Request;
import org.budo.support.http.client.Response;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.lang.util.NumberUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.weixin.message.Message;
import org.budo.weixin.message.TemplateMessage;
import org.budo.weixin.message.TextMessage;
import org.budo.weixin.util.WeixinUtil;
import org.slf4j.Logger;

import com.alibaba.fastjson.JSON;

/**
 * @author li
 * @version 1 (2015年5月28日 上午10:58:15)
 * @since Java7
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class WeixinApi implements Serializable {
    private static final long serialVersionUID = -7050788217483471499L;

    private Map<String, Integer> retryCount = new HashMap<String, Integer>();

    private static final Logger log = Slf4j.getLogger();

    private static final String LANG_ZH_CN = "zh_CN";

    private String accessToken;

    private String componentAppId;

    public WeixinApi() {}

    public String getAccessToken() {
        return accessToken;
    }

    public WeixinApi setAccessToken(String accessToken) {
        this.accessToken = accessToken;
        return this;
    }

    public String getComponentAppId() {
        return componentAppId;
    }

    public void setComponentAppId(String componentAppId) {
        this.componentAppId = componentAppId;
    }

    /**
     * 获取授权方式微信access_token和refresh_token，如果单独调用，则需要将authorizer_access_token和authorizer_refresh_token写入渠道表 { "authorizer_access_token": "aaUl5s6kAByLwgV0BhXNuIFFUqfrR8vTATsoSHukcIGqJgrc4KmMJ-JlKoC_-NKCLBvuU1cWPv4vDcLN8Z0pn5I45mpATruU0b51hzeT1f8", "expires_in": 7200, "authorizer_refresh_token": "BstnRqgTJBXb9N2aJq6L5hzfJwP406tpfahQeLNxX0w" }
     */
    public Map getAuthorizerWeixinToken(String appId, String authorizerRefreshToken, String componentAppId, String componentAccessToken) {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=" + componentAccessToken);
        Map data = MapUtil.newMap("component_appid", componentAppId, "authorizer_appid", appId, "authorizer_refresh_token", authorizerRefreshToken);
        request.setMethod(Request.POST);
        request.setRequestBody(JSON.toJSONBytes(data));
        Response response = request.execute();
        log.info("getAuthorizerWeixinToken, response=" + (null == response ? response : response.toSimpleString()));

        String result = response.getBody();
        Map<String, Object> map = JSON.parseObject(result);
        String token = StringUtil.toString(map.get("authorizer_access_token"));
        if (StringUtil.isEmpty(token)) {
            log.error("获取微信Token失败, appId={},authorizerRefreshToken={}，componentAppId={}, componentAccessToken={}, response={}", appId, authorizerRefreshToken, componentAppId, componentAccessToken, response);
        }
        return map;
    }

    /**
     * 获取微信二维码
     */
    public String getWeixinQRCode() {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + getAccessToken());
        request.setMethod(Request.POST);
        request.setRequestBody("{\"action_name\": \"QR_LIMIT_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": 123}}}".getBytes());
        Response response = request.execute();
        log.info("getWeixinQRCode, response=" + (null == response ? response : response.toSimpleString()));

        String result = response.getBody();
        Map<String, Object> map = JSON.parseObject(result);
        String ticket = StringUtil.toString(map.get("ticket"));

        if (StringUtil.isEmpty(ticket)) {
            log.error("getWeixinQRCode error, token={}, response={}", getAccessToken(), result);
            return null;
        }

        return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
    }

    /**
     * @throws AesException
     * @see org.budo.weixin.qiye.api.WeixinQiyeApi#sendMessage(org.budo.weixin.qiye.WeixinQiyeMessage)
     */
    public String sendMessage(Message message) {
        if (null == message) {
            log.error("message is null, message=" + message);
            return "message is null";
        }

        if (message instanceof TextMessage && StringUtil.isEmpty(((TextMessage) message).getContent())) {
            log.error("message.content is null, message=" + message);
            return "message.content is null";
        }

        String url = null;
        if (message instanceof TemplateMessage) {
            url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + getAccessToken();
            log.info("WeixinQiyeApi TemplateMessage To:{}", url);
        } else {
            url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + getAccessToken();
            log.info("WeixinQiyeApi Message To:{}", url);
        }

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        String messageToJson = WeixinUtil.messageToJson(message);

        request.setRequestBody(messageToJson.getBytes());
        Response response = request.execute();
        log.info("WeixinApi sendMessage To:{} Response：{}", url, response);
        if (NumberUtil.equals(200, response.getStatus())) {
            retryCount.remove(message.getId());
        } else {
            Integer count = retryCount.get(message.getId());
            count = (null == count ? 0 : count);
            if (count < 2) {
                retryCount.put(message.getId(), new Integer(count + 1));
                sendMessage(message);
            } else {
                retryCount.remove(message.getId());
            }
        }
        String string = response.getBody();
        return string;
    }

    public String createMenu(String menusToString) {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + getAccessToken());
        request.setMethod(Request.POST);
        request.setRequestBody(menusToString.getBytes());
        Response response = request.execute();

        log.info("createMenu, response=" + (null == response ? response : response.toSimpleString()));
        return response.getBody();
    }

    public String getMenu() {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/cgi-bin/menu/get?access_token=" + getAccessToken());
        request.setMethod(Request.GET);
        Response response = request.execute();

        log.info("getMenu, response=" + (null == response ? response : response.toSimpleString()));
        return response.getBody();
    }

    public String deleteMenu() {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=" + getAccessToken());
        request.setMethod(Request.GET);
        Response response = request.execute();

        log.info("deleteMenu, response=" + (null == response ? response : response.toSimpleString()));
        return response.getBody();
    }

    /**
     * 上传资源
     * @param type image voice video thumb
     * @param media File URL InputStream byte[]
     * @return 返回 {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789}
     */
    public WeixinMediaUploadResult uploadMedia(String type, Object media) {
        Request request = new Request();
        request.setMethod(Request.POST);
        request.setUrl("http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=" + getAccessToken() + "&type=" + type);
        request.setField("media", media);

        Response response = request.execute();
        WeixinMediaUploadResult weixinMediaUploadResult = new WeixinMediaUploadResult(response.getBody());
        if (!weixinMediaUploadResult.getSuccess()) {
            log.error("uploadMedia failed, response={}, request={}", response, request);
        } else {
            log.info("uploadMedia success, response={}, request={}", response, request);
        }

        return weixinMediaUploadResult;
    }

    /**
     * 微信是跳转
     * @return 返回的是跳转地址
     */
    public String getMedia(String mediaId) {
        return "https://api.weixin.qq.com/cgi-bin/media/get?access_token=" + this.getAccessToken() + "&media_id=" + mediaId;
    }

    /**
     * 微信是跳转
     * @return 返回的文件内容
     */
    public InputStream downloadMedia(String mediaId) {
        try {
            return new URL(getMedia(mediaId)).openStream();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取用户基本信息 http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html
     * @param openId
     */
    public Map<String, Object> getUserInfo(String openId) {
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + this.getAccessToken() + "&openid=" + openId + "&lang=" + LANG_ZH_CN;
        Response response = HttpUtil.get(url);
        String responseBody = StringUtil.toString(response);

        log.info("getUserInfo, responseBody=" + responseBody);
        return (Map<String, Object>) JSON.parse(responseBody);
    }

    /**
     * http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html#.E7.AC.AC.E4.BA.8C.E6.AD.A5.EF.BC.9A.E9.80.9A.E8.BF.87code.E6.8D.A2.E5.8F.96.E7.BD.91.E9.A1.B5.E6.8E.88.E6.9D.83access_token { "access_token": "OezXcEiiBSKSxW0eoylIeAsR0GmYd1awCffdHgb4fhS_KKf2CotGj2cBNUKQQvj-G0ZWEE5-uBjBz941EOPqDQy5sS_GCs2z40dnvU99Y5AI1bw2uqN--2jXoBLIM5d6L9RImvm8Vg8cBAiLpWA8Vw", "expires_in": 7200, "refresh_token": "OezXcEiiBSKSxW0eoylIeAsR0GmYd1awCffdHgb4fhS_KKf2CotGj2cBNUKQQvj-G0ZWEE5-uBjBz941EOPqDQy5sS_GCs2z40dnvU99Y5CZPAwZksiuz_6x_TfkLoXLU7kdKM2232WDXB3Msuzq1A", "openid": "oLVPpjqs9BhvzwPj5A-vTYAX3GLc", "scope": "snsapi_userinfo," }
     */
    public Map<String, Object> getSnsToken(String appId, String appSecret, String code) {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/sns/oauth2/access_token" //
                + "?appid=" + appId //
                + "&secret=" + appSecret //
                + "&code=" + code //
                + "&grant_type=authorization_code");
        request.setMethod(Request.GET);
        Response response = request.execute();
        log.info("getSnsToken, response=" + (null == response ? response : response.toSimpleString()));

        String result = response.getBody();
        Map<String, Object> map = JSON.parseObject(result);
        String token = StringUtil.toString(map.get("access_token"));

        if (StringUtil.isEmpty(token)) {
            log.error("getSnsToken 失败, appId={}, appSecret={}, code={}, response={}", appId, appSecret, code, response);
        } else {
            log.info("获取微信Token成功, token={}", token);
        }

        return map;
    }

    public Map<String, Object> getComponentSnsToken(String appId, String code, String componentAppid, String componentAccessToken) {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/sns/oauth2/component/access_token" //
                + "?appid=" + appId + "&component_appid=" + componentAppid + "&component_access_token=" + componentAccessToken + "&code=" + code + "&grant_type=authorization_code");
        request.setMethod(Request.GET);
        Response response = request.execute();
        log.info("getComponentSnsToken, response=" + (null == response ? response : response.toSimpleString()));

        String result = response.getBody();
        Map<String, Object> map = JSON.parseObject(result);
        String token = StringUtil.toString(map.get("access_token"));
        if (StringUtil.isEmpty(token)) {
            log.error("getComponentSnsToken 失败, appId={}, componentAppid={}, code={}, response={}", appId, componentAppid, code, response);
        } else {
            log.info("getComponentSnsTokenToken成功, token={}", token);
        }

        return map;
    }

    /**
     * http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html { "openid": "oLVPpjqs9BhvzwPj5A-vTYAX3GLc", "nickname": "刺猬宝宝", "sex": 1, "language": "简体中文", "city": "深圳", "province": "广东", "country": "中国", "headimgurl": "http://wx.qlogo.cn/mmopen/utpKYf69VAbCRDRlbUsPsdQN38DoibCkrU6SAMCSNx558eTaLVM8PyM6jlEGzOrH67hyZibIZPXu4BK1XNWzSXB3Cs4qpBBg18/0", "privilege": [] }
     */
    public Map<String, Object> getSnsUserInfo(String accessToken, String openId) {
        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
        Response response = HttpUtil.get(url);
        String body = response.getBody();
        Map map = JSON.parseObject(body);
        return map;
    }

    public Map<String, Object> getWeixinTicket(String type) {
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + this.getAccessToken() + "&type=" + type;
        Response response = HttpUtil.get(url);
        String body = response.getBody();
        Map map = JSON.parseObject(body);
        return map;
    }

    public byte[] getSmallAppQrCode(String path, Integer width) throws UnsupportedEncodingException {
        String url = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=" + this.getAccessToken();
        Map fields = MapUtil.stringObjectMap("path", path, "width", width);
        byte[] data = JSON.toJSONString(fields).getBytes("UTF-8");
        Response response = HttpUtil.post(url, null, data);
        log.info("WeixinApi getSmallAppQrCode To:{} Response：{}", url, response);
        return response.getResponseData();
    }

    public byte[] getSmallAppLoginQrCode(String scene, String page, Integer width, Boolean autoColor, Map lineColor) throws UnsupportedEncodingException {
        String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + this.getAccessToken();
        Map fields = MapUtil.stringObjectMap("scene", scene, "width", width, "page", page, "auto_color", autoColor, "line_color", lineColor);
        byte[] data = JSON.toJSONString(fields).getBytes("UTF-8");
        Response response = HttpUtil.post(url, null, data);
        log.info("WeixinApi getSmallAppLoginQrCode To:{} Response：{}", url, response);
        return response.getResponseData();
    }

    /**
     * 获取微信Token，
     */
    public String getWeixinToken(String appId, String appSecret) {
        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" //
                + "&appid=" + appId + "&secret=" + appSecret);
        request.setMethod(Request.GET);
        Response response = request.execute();
        log.info("getWeixinToken, response=" + (null == response ? response : response.toSimpleString()));

        String result = response.getBody();
        Map<String, Object> map = JSON.parseObject(result);
        String token = StringUtil.toString(map.get("access_token"));
        if (StringUtil.isEmpty(token)) {
            log.error("获取微信Token失败, appId={}, appSecret={}, response={}", appId, appSecret, response);
        }
        return this.accessToken = token;
    }

    public WeixinTokenEntity token(String appId, String appSecret) {
        Assert.notEmpty(appId, "#353 appId=" + appId);
        Assert.notEmpty(appSecret, "#354 appSecret=" + appSecret);

        Request request = new Request();
        request.setUrl("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" //
                + "&appid=" + appId + "&secret=" + appSecret);
        request.setMethod(Request.GET);
        Response response = request.execute();
        log.info("getWeixinToken, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        Map<String, Object> responseMap = JSON.parseObject(responseBody);
        String token = StringUtil.toString(responseMap.get("access_token"));

        if (StringUtil.isEmpty(token)) {
            log.error("#362 获取微信Token失败, appId={}, appSecret={}, response={}", appId, appSecret, response);
            return null;
        }

        WeixinTokenEntity tokenEntity = new WeixinTokenEntity();
        tokenEntity.setAccessToken(token);
        tokenEntity.setExpiresIn(NumberUtil.toInteger(responseMap.get("expires_in")));
        return tokenEntity;
    }
}